/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package se.bth.ke.firstfriend.scheduled.rating;

import java.math.BigDecimal;
import java.util.List;
import se.bth.ke.firstfriend.model.Article;
import se.bth.ke.firstfriend.model.Rating;

/**
 * This class encapsulates logic for calculating article relevance and rating.
 * It is not thread safe!
 * 
 * @author nico.rehwaldt
 */
public class ArticleRater {

    private static final int MINIMUM_VOTES = 2;
    private static final int FRACTIONAL_DIGITS = 2;

    private double globalRatingAverage = 0.0;

    public ArticleRater() {}

    /**
     * @return the globalRatingAverage
     */
    public double getGlobalRatingAverage() {
        return globalRatingAverage;
    }

    /**
     * @param globalRatingAverage the globalRatingAverage to set
     */
    public void setGlobalRatingAverage(double globalRatingAverage) {
        this.globalRatingAverage = globalRatingAverage;
    }
    
    /**
     * Calculates the relevance of an article.
     * Has to be executed within a transactional context as the article will
     * fetch all ratings.
     * 
     * @param article
     * @return
     */
    public double calculateRelevance(Article article, List<Rating> ratings) {
        if (!ratings.isEmpty() && globalRatingAverage != 0.0) {
            /* Calculates a true Bayesian estimate with:
             *
             * n - number of article ratings
             * r - average rating for the article
             * MINIMUM_VOTES - votes needed to appear in the list
             * GLOBAL_AVERAGE - the average rating of all articles
             */
            double n = (double) ratings.size();
            if (n > MINIMUM_VOTES) {
                double r = article.getRating();
                double articleSpecificSummand = (n / (n + MINIMUM_VOTES)) * r;
                double globalSpecifigSummand = (MINIMUM_VOTES / (n + MINIMUM_VOTES)) * globalRatingAverage;
                
                return articleSpecificSummand + globalSpecifigSummand;
            }
        }

        return article.getRelevance();
    }

    public double calculateAverageRating(Article article, List<Rating> ratings) {
        if (ratings != null && !ratings.isEmpty()) {
            int sum = 0;
            for (Rating r : ratings) {
                sum += r.getValue();
            }

            return roundAverageRating(sum / (double) ratings.size());
        }

        // if no other rules apply
        return article.getRating();
    }

    private double roundAverageRating(double averageRating) {
        BigDecimal decimal = new BigDecimal(averageRating);
        decimal.setScale(FRACTIONAL_DIGITS, BigDecimal.ROUND_HALF_UP);

        return decimal.doubleValue();
    }
}
